﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EpcTagLibrary.Pat
{
    public abstract class PatternFactory
    {
        private static int m_PatternsMaxSize = 25;
        private static IList<Pattern> m_Patterns = new List<Pattern>(m_PatternsMaxSize);
        private static PatternFactory[] m_Factories = InitFactories();

        public Pattern GetPattern(string patBody, bool group)
        {
            return GetPattern(patBody, group, true);
        }

        public abstract Pattern GetPattern(string patBody, bool isGroupPattern, bool isPureid);

        public static Pattern NewInstance(string uri)
        {
            return NewInstance(uri, false);
        }

        public static Pattern NewInstance(string uri, bool group)
        {
            uri = uri.ToLower();

            if (!group && ContainerHasPattern(uri))
            {
                return GetPatternFromContainer(uri);
            }

            if (!uri.StartsWith("urn:epc:pat:") && !uri.StartsWith("urn:epc:idpat:"))
            {
                throw new PatternParseException("Specified URI is not an EPC pattern URI.");
            }

            string PatBody;
            bool IsPureId = false;

            if (uri.StartsWith("urn:epc:pat:"))
            {
                PatBody = uri.Substring("urn:epc:pat:".Length);
            }
            else
            {
                PatBody = uri.Substring("urn:epc:idpat:".Length);
                IsPureId = true;
            }

            Pattern P = null;

            foreach (PatternFactory Factory in m_Factories)
            {
                try
                {
                    P = Factory.GetPattern(PatBody, group, IsPureId);

                    if (P != null)
                    {
                        break;
                    }
                }
                catch (Exception)
                {
                }
            }

            if (P == null)
            {
                throw new PatternParseException("Could not parse pattern URI.");
            }

            P.Uri = uri;
            P.IsGrouping = group;
            P.IsPureId = IsPureId;

            if (!group)
            {
                AddPatternToContainer(P);
            }

            return P;
        }

        public static void SetContainerMaxSize(int max)
        {
            m_PatternsMaxSize = max;

            while (m_Patterns.Count > m_PatternsMaxSize)
            {
                m_Patterns.RemoveAt(0);
            }
        }

        public static Boolean ContainerHasPattern(string uri)
        {
            foreach (Pattern Pat in m_Patterns)
            {
                if (Pat.Uri.ToLower().Equals(uri.ToLower()))
                {
                    return true;
                }
            }

            return false;
        }

        public static Pattern GetPatternFromContainer(string uri)
        {
            foreach (Pattern Pat in m_Patterns)
            {
                if (Pat.Uri.ToLower().Equals(uri.ToLower()))
                {
                    return Pat;
                }
            }

            return null;
        }

        public static void AddPatternToContainer(Pattern p)
        {
            if (!p.IsGrouping)
            {
                m_Patterns.Add(p);

                while (m_Patterns.Count > m_PatternsMaxSize)
                {
                    m_Patterns.RemoveAt(0);
                }
            }
        }

        private static PatternFactory[] InitFactories()
        {
            PatternFactory[] Result = new PatternFactory[7];

            Result[0] = new GID.Factory();
            Result[1] = new SGTIN.Factory();
            Result[2] = new SSCC.Factory();
            Result[3] = new SGLN.Factory();
            Result[4] = new GRAI.Factory();
            Result[5] = new GIAI.Factory();
            Result[6] = new DOD.Factory();

            return Result;
        }
    }
}
